package com.bzkj.document.utils.common;

import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONException;
import com.alibaba.fastjson2.JSONObject;
import com.bzkj.common.core.domain.model.LoginUser;
import com.bzkj.common.core.page.TableDataInfo;
import com.bzkj.document.domain.BasicQueryDTO;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeParseException;
import java.util.*;

/**
 * @projectName: prt-wms
 * @package: com.bzkj.document.utils
 * @className: DataProcessUtils
 * @description: NCC单据数据处理工具类
 * @author: C1309
 * @createDate: 2024年12月17日
 * @version: v1.0
 */
public class DataProcessUtils {

    /**
     * 处理字段，将字段值转换为JSONObject对象，并添加isMap属性
     *
     * @param sourceMap 原数据
     * @param targetMap 目标数据
     * @param fieldName 字段名
     */
    public static void processField(Map<String, Object> sourceMap, Map<String, Object> targetMap, String fieldName) {
        // 获取字段值
        Object fieldValue = sourceMap.get(fieldName);

        if (fieldValue instanceof LinkedHashMap) {
            // 处理 LinkedHashMap 类型（通常是嵌套 JSON 对象）
            JSONObject jsonMap = new JSONObject((LinkedHashMap<?, ?>) fieldValue);
            jsonMap.put("isMap", true);
            targetMap.put(fieldName, jsonMap);
        } else if (fieldValue instanceof String) {
            // 处理 String 类型
            String fieldStr = (String) fieldValue;
            String trimmedStr = fieldStr.trim();

            if (trimmedStr.startsWith("{") && trimmedStr.endsWith("}")) {
                // 字符串是 JSON 对象，尝试解析
                try {
                    JSONObject fieldJson = JSON.parseObject(fieldStr);
                    fieldJson.put("isMap", true);
                    targetMap.put(fieldName, fieldJson);
                } catch (JSONException e) {
                    // 解析失败，抛出带有详细信息的异常
                    throw new JSONException("字段 " + fieldName + " 的值无法解析为 JSON: " + fieldStr, e);
                }
            } else {
                // 普通字符串，不是 JSON
                JSONObject field = new JSONObject();
                field.put("isMap", false);
                field.put("value", fieldStr);
                targetMap.put(fieldName, field);
            }
        } else if (fieldValue == null) {
            // 处理 null 值
            targetMap.put(fieldName, "");
        } else {
            // 其他类型，转换为字符串
            JSONObject field = new JSONObject();
            field.put("isMap", false);
            field.put("code", fieldValue.toString());
            targetMap.put(fieldName, field);
        }
    }

    /**
     * 获取字符串字段值
     *
     * @param sourceMap 订单的JSONObject对象
     * @param fieldName 字段名
     * @param targetMap 用于存放结果的Map
     */
    public static void getStringField(JSONObject sourceMap, String fieldName, Map<String, Object> targetMap) {
        String fieldValue = sourceMap.getString(fieldName);
        if (fieldValue != null) {
            targetMap.put(fieldName, fieldValue);
        } else {
            targetMap.put(fieldName, "");
        }
    }

    /*================================================== 分割线 ==================================================*/

    /**
     * 处理日期字段，将日期格式化为指定格式
     *
     * @param sourceMap 订单的JSONObject对象
     * @param fieldName 日期字段名
     * @param sdf       日期格式化对象
     * @param targetMap 用于存放结果的Map
     */
    public static void handleDateField(JSONObject sourceMap, String fieldName, SimpleDateFormat sdf, Map<String, Object> targetMap) {
        Date date = sourceMap.getDate(fieldName);
        if (date != null) {
            targetMap.put(fieldName, sdf.format(date));
        } else {
            targetMap.put(fieldName, "");
        }
    }

    /*================================================== 分割线 ==================================================*/

    /**
     * 获取指定字段的值并处理小数点后四位
     *
     * @param sourceMap 订单的JSONObject对象
     * @param fieldName 需要获取和处理的字段名
     * @param targetMap 用于存放结果的Map
     */
    public static void getFormattedDouble(JSONObject sourceMap, String fieldName, Map<String, Object> targetMap) {
        try {
            // 1. 检查字段是否存在（Fastjson 使用 containsKey）
            if (!sourceMap.containsKey(fieldName)) {
                targetMap.put(fieldName, 0.0);
                return;
            }

            // 2. 获取值并处理 null 或空字符串
            Object value = sourceMap.get(fieldName);
            if (value == null || value.toString().trim().isEmpty()) {
                targetMap.put(fieldName, 0.0);
                return;
            }

            // 3. 转换为 BigDecimal（避免 Double 精度问题）
            BigDecimal decimalValue;
            try {
                decimalValue = new BigDecimal(value.toString().trim());
            } catch (NumberFormatException e) {
                throw new IllegalArgumentException("NCC字段异常：【" + fieldName + "】字段值必须是有效数字！");
            }

            // 4. 四舍五入保留4位小数
            double roundedValue = decimalValue.setScale(4, RoundingMode.HALF_UP).doubleValue();
            targetMap.put(fieldName, roundedValue);

        } catch (Exception e) {
            // 更友好的错误提示
            if (e instanceof IllegalArgumentException) {
                throw (IllegalArgumentException) e;
            }
            throw new RuntimeException("处理字段【" + fieldName + "】时出错: " + e.getMessage(), e);
        }
    }

    /*================================================== 分割线 ==================================================*/

    /**
     * 校验字段是否为空
     *
     * @param obj      要校验的对象
     * @param field    字段名称
     * @param errorMsg 错误提示信息
     */
    public static void checkNotNull(JSONObject obj, String field, String errorMsg) {
        if (obj == null || obj.getString(field) == null || obj.getString(field).isEmpty()) {
            throw new IllegalArgumentException(errorMsg);
        }
    }

    /*================================================== 分割线 ==================================================*/

    /**
     * 处理本地查询日期范围
     *
     * @param requestDTO 查询条件
     * @param <T>        查询条件
     */
    public static <T extends BasicQueryDTO> void handleDateRange(T requestDTO) {
        // 获取账单日期字段
        String billsDate = requestDTO.getDbilldate();

        if (billsDate != null && !billsDate.trim().isEmpty()) {
            // 按照 "~" 分割日期字符串
            String[] split = billsDate.split("~");

            // 检查分割后的数组长度
            if (split.length != 2) {
                throw new IllegalArgumentException("日期范围格式错误，应为：yyyy-MM-dd~yyyy-MM-dd");
            }

            SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
            try {
                // 解析开始日期和结束日期
                Date startDate = formatter.parse(split[0].trim() + " 00:00:00");
                Date endDate = formatter.parse(split[1].trim() + " 23:59:59");

                // 设置到 requestDTO 中
                requestDTO.setStartDate(startDate);
                requestDTO.setEndDate(endDate);
            } catch (ParseException e) {
                // 抛出更清晰的异常信息
                throw new RuntimeException("日期格式错误，请检查日期格式是否为：yyyy-MM-dd", e);
            }
        }
    }

    /*================================================== 分割线 ==================================================*/

    /**
     * 获取当前日期
     *
     * @return 当前日期字符串
     */
    public static String getLocalDate() {
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        return simpleDateFormat.format(new Date());
    }

    /*================================================== 分割线 ==================================================*/

    /**
     * 添加表头新增的额外信息
     *
     * @param sourceMap 原表头信息
     * @param loginUser 登录用户
     */
    public static void addHeaderInfo(Map<String, Object> sourceMap, LoginUser loginUser) {
        // 用户名
        String username = loginUser.getUsername();
        // 部门id(wms)
        Long deptId = loginUser.getDeptId();
        // 用户id (wms)
        Long userId = loginUser.getUserId();
        //添加额外信息
        sourceMap.put("createBy", username);
        sourceMap.put("deptId", deptId);
        sourceMap.put("userId", userId);
        sourceMap.put("createTime", getLocalDate());

    }

    /*================================================== 分割线 ==================================================*/

    /**
     * 添加表体新增的额外信息
     *
     * @param sourceMap 原表体信息
     * @param loginUser 登录用户
     */
    public static void addDetailInfo(Map<String, Object> sourceMap, LoginUser loginUser) {
        // 用户名
        String username = loginUser.getUsername();
        //添加额外信息
        sourceMap.put("createBy", username);
        sourceMap.put("createTime", getLocalDate());
    }

    /*================================================== 分割线 ==================================================*/

    /**
     * 添加表体额外信息（采购入库单明细）
     *
     * @param detailMapList  详细信息列表，每个元素是一个 Map，可能包含 "cmaterialoid" 子 Map。
     * @param requestMapList 请求参数列表，每个元素是一个 Map，可能包含 "pk_material" 子 Map。
     */
    public static void addParamsInfo(List<Map<String, Object>> detailMapList, List<Map<String, Object>> requestMapList) {
        if (detailMapList == null || requestMapList == null) {
            return; // 如果输入列表为空，直接返回
        }

        // 遍历详细信息列表
        for (Map<String, Object> detailMap : detailMapList) {
            if (detailMap == null) {
                continue; // 跳过空的 detailMap
            }

            // 获取 "cmaterialoid" 子 Map，并提取对应的 pk 或 code
            Map<String, Object> cmaterialoid = getNestedMap(detailMap, "cmaterialoid");
            String detailPk = getPkOrCode(cmaterialoid);

            // 遍历请求参数列表
            for (Map<String, Object> requestMap : requestMapList) {
                if (requestMap == null) {
                    continue; // 跳过空的 requestMap
                }

                // 获取 "pk_material" 子 Map，并提取对应的 pk 或 code
                Map<String, Object> pkMaterial = getNestedMap(requestMap, "pk_material");
                String requestPk = getPkOrCode(pkMaterial);

                // 如果 pk 或 code 匹配，将相关字段从请求参数复制到详细信息中
                if (detailPk.equals(requestPk)) {
                    copyValues(requestMap, detailMap, "sto_id", "stu_class_id");
                }
            }
        }
    }

    /**
     * 获取 pk 或 code
     *
     * @param map map
     * @param key 主键
     * @return 结果
     */
    public static String getPkOrCode(Map<String, Object> map, String key) {
        Map<String, Object> nestedMap = getNestedMap(map, key);
        return getPkOrCode(nestedMap);
    }

    /**
     * 从 Map 中获取嵌套的子 Map。
     *
     * @param map 包含嵌套 Map 的父 Map。
     * @param key 子 Map 的键。
     * @return 如果存在嵌套 Map，则返回该 Map；否则返回 null。
     */
    public static Map<String, Object> getNestedMap(Map<String, Object> map, String key) {
        if (map == null) {
            return null; // 如果父 Map 为空，返回 null
        }
        @SuppressWarnings("unchecked")
        Map<String, Object> nestedMap = (Map<String, Object>) map.get(key);
        return nestedMap;
    }

    /**
     * 从 Map 中提取 "pk" 或 "code" 字段的值。
     *
     * @param map 包含 "pk" 和 "code" 字段的 Map。
     * @return 如果 Map 存在 "pk" 或 "code"，返回其值；否则返回空字符串。
     */
    public static String getPkOrCode(Map<String, Object> map) {
        if (map == null) {
            return ""; // 如果 Map 为空，返回空字符串
        }
        boolean isMapped = (boolean) map.getOrDefault("isMap", false); // 判断是否映射
        return (String) map.getOrDefault(isMapped ? "pk" : "code", ""); // 根据 isMap 获取对应值
    }

    /**
     * 将指定的字段从源 Map 复制到目标 Map。
     *
     * @param source 源 Map。
     * @param target 目标 Map。
     * @param keys   要复制的字段键数组。
     */
    private static void copyValues(Map<String, Object> source, Map<String, Object> target, String... keys) {
        if (source == null || target == null || keys == null) {
            return; // 如果任一参数为空，直接返回
        }
        for (String key : keys) {
            if (source.containsKey(key)) {
                // 复制字段值
                target.put(key, source.get(key));
            }
        }
    }


    /**
     * 修改操作添加额外的信息
     *
     * @param sourceMap 原表体信息
     * @param loginUser 登录用户
     */
    public static void updateInfo(Map<String, Object> sourceMap, LoginUser loginUser) {
        // 用户名
        String username = loginUser.getUsername();
        //添加额外信息
        sourceMap.put("updateBy", username);
        sourceMap.put("updateTime", getLocalDate());
    }


    /**
     * 对NCC返回的数据进行分页
     *
     * @param pageNum  当前页
     * @param pageSize 页大小
     * @param list     数据
     * @return 分页后的数据
     */
    public static TableDataInfo startPageInfo(int pageNum, int pageSize, List<Map<String, Object>> list) {
        // 日期格式化器
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");

        // 创建 TableDataInfo 对象
        TableDataInfo tableDataInfo = new TableDataInfo();

        // 按 dbilldate 倒序排序
        // 按 dmakedate 倒序排序
        list.sort((map1, map2) -> {
            // 获取 header 数据
            @SuppressWarnings("unchecked")
            Map<String, Object> details1 = (Map<String, Object>) map1.get("header");
            @SuppressWarnings("unchecked")
            Map<String, Object> details2 = (Map<String, Object>) map2.get("header");

            if (details1 == null || details2 == null) {
                return details1 == null ? 1 : -1;
            }

            // 获取 dmakedate 字段
            String dateStr1 = (String) details1.get("dmakedate");
            String dateStr2 = (String) details2.get("dmakedate");

            if (dateStr1 == null || dateStr2 == null) {
                return dateStr1 == null ? 1 : -1;
            }

            try {
                // 解析为 LocalDateTime
                LocalDateTime date1 = LocalDateTime.parse(dateStr1, formatter);
                LocalDateTime date2 = LocalDateTime.parse(dateStr2, formatter);
                // 倒序比较
                return date2.compareTo(date1);
            } catch (DateTimeParseException e) {
                throw new RuntimeException("日期格式错误: " + e.getMessage(), e);
            }
        });

        // 分页计算
        int total = list.size();
        int fromIndex = (pageNum - 1) * pageSize;
        int toIndex = Math.min(fromIndex + pageSize, total);

        // 检查分页范围
        if (fromIndex >= total) {
            tableDataInfo.setCode(200);
            tableDataInfo.setMsg("Oops~ 当前条件下没有找到单据，试试调整查询条件吧！");
            tableDataInfo.setRows(Collections.emptyList());
            tableDataInfo.setTotal(total);
            return tableDataInfo;
        }

        // 获取当前页数据
        List<Map<String, Object>> pageData = list.subList(fromIndex, toIndex);

        // 封装分页结果
        tableDataInfo.setCode(200);
        tableDataInfo.setMsg("查询成功！");
        tableDataInfo.setRows(pageData);
        tableDataInfo.setTotal(total);
        return tableDataInfo;
    }
}